/**
 * DOM 操作工具类
 */
import { camelToKebab } from './index'
class Dom {
  private element: HTMLElement
  /**
   * 创建一个 Dom 实例
   * @param selector - CSS选择器、HTML元素或标签名称
   */
  constructor(selector: string | HTMLElement) {
    if (typeof selector === 'string') {
      // 如果是HTML标签字符串，创建新元素
      if (selector.startsWith('<') && selector.endsWith('>')) {
        const tagName = selector.slice(1, -1)
        this.element = document.createElement(tagName)
      } else {
        // 否则查询现有元素
        const el = document.querySelector(selector)
        if (!el) throw new Error(`元素未找到: ${selector}`)
        this.element = el as HTMLElement
      }
    } else {
      this.element = selector
    }
  }

  /** 获取原始 DOM 元素 */
  get el(): HTMLElement {
    return this.element
  }

  /**
   * 获取文本内容
   * @returns 文本内容
   */
  get text(): this | string {
    return this.element.textContent || ''
  }
  /**
   * 设置文本内容
   * @param text - 要设置的文本内容（可选）
   */
  set text(text: string) {
    this.element.textContent = text
  }
  /**
   * 设置文本内容
   * @param text - 要设置的文本内容（可选）
   * @returns 当前实例
   */
  setText(text?: string): this {
    if (text === undefined) return this
    this.element.textContent = text
    return this
  }
  /**
   * 设置或获取HTML内容
   * @param html - 要设置的HTML字符串（可选）
   * @returns 当前实例或HTML内容
   */
  get html(): string {
    return this.element.innerHTML
  }
  set html(html: string) {
    this.element.innerHTML = html
  }
  /**
   * 设置innerHTML
   * @param html - 要设置的html（可选）
   * @returns 当前实例
   */
  setHtml(html?: string): this | string {
    if (html === undefined) return this
    this.element.innerHTML = html
    return this
  }

  /**
   * 添加CSS类
   * @param className - 要添加的类名
   * @returns 当前实例
   */
  addClass(className: string): this {
    this.element.classList.add(className)
    return this
  }

  /**
   * 移除CSS类
   * @param className - 要移除的类名
   * @returns 当前实例
   */
  removeClass(className: string): this {
    this.element.classList.remove(className)
    return this
  }

  /**
   * 切换CSS类
   * @param className - 要切换的类名
   * @returns 当前实例
   */
  toggleClass(className: string): this {
    this.element.classList.toggle(className)
    return this
  }

  /**
   * 检查是否存在CSS类
   * @param className - 要检查的类名
   * @returns 是否存在该类
   */
  hasClass(className: string): boolean {
    return this.element.classList.contains(className)
  }

  /**
   * 设置或获取属性值
   * @param name - 属性名
   * @param value - 属性值（可选）
   * @returns 当前实例或属性值
   */
  attr(name: string, value?: string): this | string | null {
    if (value === undefined) return this.element.getAttribute(name)
    this.element.setAttribute(name, value)
    return this
  }

  /**
   * 移除属性
   * @param name - 属性名
   * @returns 当前实例
   */
  removeAttr(name: string): this {
    this.element.removeAttribute(name)
    return this
  }

  /**
   * 设置或获取行内样式
   * @param property - 样式属性名或样式对象(minHeight等)
   * @param value - 样式值（可选）
   * @returns 当前实例或样式值
   *
   */
  inlineStyle(property: string | Partial<CSSStyleDeclaration>, value?: string): this {
    if (typeof property === 'string') {
      if (value === undefined) return this
      this.element.style.setProperty(property, value)
    } else {
      for (const key in property) {
        if (Object.prototype.hasOwnProperty.call(property, key)) {
          const val = property[key as keyof CSSStyleDeclaration]
          if (val !== undefined) {
            this.element.style.setProperty(camelToKebab(key), val as string)
          }
        }
      }
    }
    return this
  }
  /**
   * 设置或获取样式表规则
   * @param name - 规则名
   * @param rule - 样式对象(min-height等)
   * @returns 当前实例
   */
  styleSheetRule(name: string, rule: Partial<CSSStyleDeclaration>): this {
    const sheet = document.styleSheets[0] as CSSStyleSheet
    const index = sheet.cssRules.length
    // 需要排除的内置属性
    const excludeProps = ['length', 'parentRule', 'cssText', 'cssFloat']
    // 构建CSS声明字符串
    const declarations = Object.entries(rule)
      .filter(
        ([key]) =>
          // 过滤掉内置属性和数字索引
          !excludeProps.includes(key) &&
          isNaN(Number(key)) &&
          // 保留有效的CSS属性（可选）
          key in document.documentElement.style,
      )
      .map(([key, value]) => `${camelToKebab(key)}: ${value};`)
      .join(' ')
    sheet.insertRule(`${name} { ${declarations} }`, index)
    return this
  }
  /**
   * @param property - 样式属性名
   * @returns 样式值
   */

  get_css(property: string): string {
    return this.element.style.getPropertyValue(property)
  }
  /**
   * 添加事件监听器
   * @param event - 事件类型
   * @param handler - 事件处理函数
   * @returns 当前实例
   */
  on(event: string, handler: EventListener): this {
    this.element.addEventListener(event, handler)
    return this
  }

  /**
   * 移除事件监听器
   * @param event - 事件类型
   * @param handler - 事件处理函数
   * @returns 当前实例
   */
  off(event: string, handler: EventListener): this {
    this.element.removeEventListener(event, handler)
    return this
  }

  /**
   * 追加子元素
   * @param child - 要追加的子元素（Dom实例或HTML元素）
   * @returns 当前实例
   */
  append(child: Dom | HTMLElement): this {
    if (child instanceof Dom) {
      this.element.appendChild(child.el)
    } else {
      this.element.appendChild(child)
    }
    return this
  }

  /**
   * 从DOM中移除元素
   * @returns 当前实例
   */
  remove(): this {
    this.element.remove()
    return this
  }

  /**
   * 显示元素
   * @returns 当前实例
   */
  show(): this {
    this.inlineStyle('display', '')
    return this
  }

  /**
   * 隐藏元素
   * @returns 当前实例
   */
  hide(): this {
    this.inlineStyle('display', 'none')
    return this
  }

  /**
   * 查找子元素
   * @param selector - CSS选择器
   * @returns 匹配的子元素或null
   */
  find(selector: string): Dom | null {
    const el = this.element.querySelector(selector)
    return el ? new Dom(el as HTMLElement) : null
  }
}

/**
 * 创建新的DOM元素
 * @param tagName - 标签名称
 * @returns Dom实例
 */
export function create(tagName: string): Dom {
  return new Dom(`<${tagName}>`)
}

/**
 * 选择现有DOM元素
 * @param selector - CSS选择器
 * @returns Dom实例
 */
export function select(selector: string): Dom {
  return new Dom(selector)
}

// 导出Dom类
export default Dom
